package com.pactera.demo.security.config;

import com.pactera.demo.base.entity.LoginUser;
import com.pactera.demo.base.service.ILoginUserService;
import com.pactera.demo.common.dto.ResponseResult;
import com.pactera.demo.common.enums.StatusCodeEnum;
import com.pactera.demo.common.util.JwtTool;
import com.pactera.demo.common.util.ResponseUtil;
import com.pactera.demo.security.filter.JsonLoginPostProcessor;
import com.pactera.demo.security.filter.JwtAuthenticationFilter;
import com.pactera.demo.security.filter.LoginPostProcessor;
import com.pactera.demo.security.filter.PreLoginFilter;
import com.pactera.demo.security.handler.SimpleAccessDeniedHandler;
import com.pactera.demo.security.handler.SimpleAuthenticationEntryPoint;
import com.pactera.demo.security.handler.SimpleLogoutHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.social.security.SpringSocialConfigurer;

import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 *
 * </p>
 *
 * @author chang.zhou
 * @version 1.0
 * @date Created in 2020/2/29 15:18
 */
@Slf4j
@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class WebBootSecurityConfiguration {

    private static final String LOGIN_PROCESSING_URL = "/process";


    @Bean
    public JsonLoginPostProcessor jsonLoginPostProcessor(){
        return new JsonLoginPostProcessor();
    }

    @Bean
    public PreLoginFilter preLoginFilter(LoginPostProcessor loginPostProcessor){
        return new PreLoginFilter(LOGIN_PROCESSING_URL, loginPostProcessor);
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }


    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        return (request,response,authentication) -> {
            if(response.isCommitted()){
                return;
            }
            LoginUser principal = (LoginUser) authentication.getPrincipal();
            Map<String,String> map = new HashMap<>();
            map.put("token", JwtTool.createToken(principal.getUserId(), principal.getPassword()));
            map.put("name", principal.getUser() == null ? null : principal.getUser().getName());
            ResponseUtil.jsonWriter(response, ResponseResult.success(map));
        };
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler(){
        return (request,response,exception) -> {
            if(response.isCommitted()){
                return;
            }
            log.info("AuthenticationFailureHandler -> AuthenticationException -> {}", exception.getClass().getName());
            ResponseResult responseResult = null;
            if(exception instanceof BadCredentialsException){
                responseResult = ResponseResult.fail(StatusCodeEnum.USERNAME_OR_PASSWORD_ERROR);
            }else{
                responseResult = ResponseResult.fail(StatusCodeEnum.UNAUTHENTICATED_ACTION);
            }
            ResponseUtil.jsonWriter(response, responseResult);
        };
    }

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter(){
        return new JwtAuthenticationFilter();
    }

    @Configuration
    @Order(SecurityProperties.BASIC_AUTH_ORDER)
    static class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{
        @Autowired
        private FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource;
        @Autowired
        private AccessDecisionManager accessDecisionManager;
        @Autowired
        private PreLoginFilter preLoginFilter;
        @Autowired
        private ILoginUserService loginUserService;
        @Autowired
        private AuthenticationSuccessHandler authenticationSuccessHandler;
        @Autowired
        private AuthenticationFailureHandler authenticationFailureHandler;
        @Autowired
        private JwtAuthenticationFilter jwtAuthenticationFilter;
        @Autowired
        private SpringSocialConfigurer springSocialConfigurer;

        @Override
        protected void configure(HttpSecurity http) throws Exception {
                //关闭 csrf
            http.csrf().disable()
                    //允许跨域
                    .cors()
                    .and()
                    //设置session模式为无状态
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    //设置 无权限的处理器
                    .exceptionHandling().accessDeniedHandler(new SimpleAccessDeniedHandler()).authenticationEntryPoint(new SimpleAuthenticationEntryPoint())
                    .and()
                    //注册核心权限校验器
                    .authorizeRequests().anyRequest().authenticated().withObjectPostProcessor(filterSecurityInterceptorObjectPostProcessor())
                    .and()
                    //添加json方式登录
                    .addFilterBefore(preLoginFilter, UsernamePasswordAuthenticationFilter.class)
                    //添加JWT校验器
                    .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                    //设置登录地址
                    .formLogin().loginProcessingUrl(LOGIN_PROCESSING_URL).successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler)
                    .and()
                    //指定 subject的获取类
                    .userDetailsService(loginUserService)
                    //注册社交平台登录
                    .apply(this.springSocialConfigurer)
                    .and()
                    //退出系统的处理方式
                    .logout().addLogoutHandler(new SimpleLogoutHandler())
                    .logoutSuccessHandler((x, y, z) -> ResponseUtil.jsonWriter(y, ResponseResult.success("退出成功")));

        }

        /**
         * 自定义 FilterSecurityInterceptor  ObjectPostProcessor 以替换默认配置达到动态权限的目的
         *
         * @return ObjectPostProcessor
         */
        private ObjectPostProcessor<FilterSecurityInterceptor> filterSecurityInterceptorObjectPostProcessor() {
            return new ObjectPostProcessor<FilterSecurityInterceptor>() {
                @Override
                public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                    object.setAccessDecisionManager(accessDecisionManager);
                    object.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
                    return object;
                }
            };
        }
    }

}
